﻿using Nop.Admin.Extensions;
using Nop.Admin.Models.Common;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Infrastructure;
using Nop.Services.Common;
using Nop.Services.Configuration;
using Nop.Services.Customers;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Security;
using Nop.Services.Seo;
using Nop.Services.Stores;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Kendoui;
using Nop.Web.Framework.Security;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;

namespace Nop.Admin.Controllers
{
	public partial class CommonController : BaseAdminController
	{
		#region Fields
		private readonly ICustomerService _customerService;
		private readonly IUrlRecordService _urlRecordService;
		private readonly IWebHelper _webHelper;
		private readonly IDateTimeHelper _dateTimeHelper;
		private readonly ILanguageService _languageService;
		private readonly IWorkContext _workContext;
		private readonly IStoreContext _storeContext;
		private readonly IPermissionService _permissionService;
		private readonly ILocalizationService _localizationService;
		private readonly ISearchTermService _searchTermService;
		private readonly ISettingService _settingService;
		private readonly IStoreService _storeService;
		private readonly HttpContextBase _httpContext;
		private readonly IMaintenanceService _maintenanceService;

		#endregion

		#region Constructors

		public CommonController(
			ICustomerService customerService,
			IUrlRecordService urlRecordService,
			IWebHelper webHelper,
			IDateTimeHelper dateTimeHelper,
			ILanguageService languageService,
			IWorkContext workContext,
			IStoreContext storeContext,
			IPermissionService permissionService,
			ILocalizationService localizationService,
			ISearchTermService searchTermService,
			ISettingService settingService,
			IStoreService storeService,
			HttpContextBase httpContext,
			IMaintenanceService maintenanceService)
		{
			this._customerService = customerService;
			this._urlRecordService = urlRecordService;
			this._webHelper = webHelper;
			this._dateTimeHelper = dateTimeHelper;
			this._languageService = languageService;
			this._workContext = workContext;
			this._storeContext = storeContext;
			this._permissionService = permissionService;
			this._localizationService = localizationService;
			this._searchTermService = searchTermService;
			this._settingService = settingService;
			this._storeService = storeService;
			this._httpContext = httpContext;
			this._maintenanceService = maintenanceService;
		}

		#endregion

		#region Utitlies

		private bool IsDebugAssembly(Assembly assembly)
		{
			var attribs = assembly.GetCustomAttributes(typeof(System.Diagnostics.DebuggableAttribute), false);

			if (attribs.Length > 0)
			{
				var attr = attribs[0] as System.Diagnostics.DebuggableAttribute;
				if (attr != null)
				{
					return attr.IsJITOptimizerDisabled;
				}
			}

			return false;
		}

		private DateTime GetBuildDate(Assembly assembly, TimeZoneInfo target = null)
		{
			var filePath = assembly.Location;

			const int cPeHeaderOffset = 60;
			const int cLinkerTimestampOffset = 8;

			var buffer = new byte[2048];

			using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
			{
				stream.Read(buffer, 0, 2048);
			}

			var offset = BitConverter.ToInt32(buffer, cPeHeaderOffset);
			var secondsSince1970 = BitConverter.ToInt32(buffer, offset + cLinkerTimestampOffset);
			var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

			var linkTimeUtc = epoch.AddSeconds(secondsSince1970);

			var tz = target ?? TimeZoneInfo.Local;
			var localTime = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tz);

			return localTime;
		}

		#endregion

		#region Methods

		public virtual ActionResult SystemInfo()
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			var model = new SystemInfoModel();
			model.NopVersion = NopVersion.CurrentVersion;
			try
			{
				model.OperatingSystem = Environment.OSVersion.VersionString;
			}
			catch (Exception) { }
			try
			{
				model.AspNetInfo = RuntimeEnvironment.GetSystemVersion();
			}
			catch (Exception) { }
			try
			{
				model.IsFullTrust = AppDomain.CurrentDomain.IsFullyTrusted.ToString();
			}
			catch (Exception) { }
			model.ServerTimeZone = TimeZone.CurrentTimeZone.StandardName;
			model.ServerLocalTime = DateTime.Now;
			model.UtcTime = DateTime.UtcNow;
			model.CurrentUserTime = _dateTimeHelper.ConvertToUserTime(DateTime.Now);
			model.HttpHost = _webHelper.ServerVariables("HTTP_HOST");
			foreach (var key in _httpContext.Request.ServerVariables.AllKeys)
			{
				if (key.StartsWith("ALL_")) continue;

				model.ServerVariables.Add(new SystemInfoModel.ServerVariableModel
				{
					Name = key,
					Value = _httpContext.Request.ServerVariables[key]
				});
			}
			//Environment.GetEnvironmentVariable("USERNAME");

			var trustLevel = CommonHelper.GetTrustLevel();

			foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
			{
				var loadedAssembly = new SystemInfoModel.LoadedAssembly
				{
					FullName = assembly.FullName,

				};
				//ensure no exception is thrown
				try
				{
					var canGetLocation = trustLevel >= AspNetHostingPermissionLevel.High && !assembly.IsDynamic;
					loadedAssembly.Location = canGetLocation ? assembly.Location : null;
					loadedAssembly.IsDebug = IsDebugAssembly(assembly);
					loadedAssembly.BuildDate = canGetLocation ? (DateTime?)GetBuildDate(assembly, TimeZoneInfo.Local) : null;
				}
				catch (Exception) { }
				model.LoadedAssemblies.Add(loadedAssembly);
			}

			return View(model);
		}

		public virtual ActionResult Warnings()
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			var model = new List<SystemWarningModel>();

			//store URL
			var currentStoreUrl = _storeContext.CurrentStore.Url;
			if (!String.IsNullOrEmpty(currentStoreUrl) &&
				(currentStoreUrl.Equals(_webHelper.GetStoreLocation(false), StringComparison.InvariantCultureIgnoreCase)
				||
				currentStoreUrl.Equals(_webHelper.GetStoreLocation(true), StringComparison.InvariantCultureIgnoreCase)
				))
				model.Add(new SystemWarningModel
				{
					Level = SystemWarningLevel.Pass,
					Text = _localizationService.GetResource("Admin.System.Warnings.URL.Match")
				});
			else
				model.Add(new SystemWarningModel
				{
					Level = SystemWarningLevel.Warning,
					Text = string.Format(_localizationService.GetResource("Admin.System.Warnings.URL.NoMatch"), currentStoreUrl, _webHelper.GetStoreLocation(false))
				});

			////performance settings
			//if ( _storeService.GetAllStores().Count == 1)
			//{
			//    model.Add(new SystemWarningModel
			//    {
			//        Level = SystemWarningLevel.Warning,
			//        Text = _localizationService.GetResource("Admin.System.Warnings.Performance.IgnoreStoreLimitations")
			//    });
			//}
			//validate write permissions (the same procedure like during installation)
			var dirPermissionsOk = true;
			var dirsToCheck = FilePermissionHelper.GetDirectoriesWrite();
			foreach (string dir in dirsToCheck)
				if (!FilePermissionHelper.CheckPermissions(dir, false, true, true, false))
				{
					model.Add(new SystemWarningModel
					{
						Level = SystemWarningLevel.Warning,
						Text = string.Format(_localizationService.GetResource("Admin.System.Warnings.DirectoryPermission.Wrong"), WindowsIdentity.GetCurrent().Name, dir)
					});
					dirPermissionsOk = false;
				}
			if (dirPermissionsOk)
				model.Add(new SystemWarningModel
				{
					Level = SystemWarningLevel.Pass,
					Text = _localizationService.GetResource("Admin.System.Warnings.DirectoryPermission.OK")
				});

			var filePermissionsOk = true;
			var filesToCheck = FilePermissionHelper.GetFilesWrite();
			foreach (string file in filesToCheck)
				if (!FilePermissionHelper.CheckPermissions(file, false, true, true, true))
				{
					model.Add(new SystemWarningModel
					{
						Level = SystemWarningLevel.Warning,
						Text = string.Format(_localizationService.GetResource("Admin.System.Warnings.FilePermission.Wrong"), WindowsIdentity.GetCurrent().Name, file)
					});
					filePermissionsOk = false;
				}
			if (filePermissionsOk)
				model.Add(new SystemWarningModel
				{
					Level = SystemWarningLevel.Pass,
					Text = _localizationService.GetResource("Admin.System.Warnings.FilePermission.OK")
				});

			////machine key
			//try
			//{
			//    var machineKeySection = ConfigurationManager.GetSection("system.web/machineKey") as MachineKeySection;
			//    var machineKeySpecified = machineKeySection != null &&
			//        !String.IsNullOrEmpty(machineKeySection.DecryptionKey) &&
			//        !machineKeySection.DecryptionKey.StartsWith("AutoGenerate", StringComparison.InvariantCultureIgnoreCase);

			//    if (!machineKeySpecified)
			//    {
			//        model.Add(new SystemWarningModel
			//        {
			//            Level = SystemWarningLevel.Warning,
			//            Text = _localizationService.GetResource("Admin.System.Warnings.MachineKey.NotSpecified")
			//        });
			//    }
			//    else
			//    {
			//        model.Add(new SystemWarningModel
			//        {
			//            Level = SystemWarningLevel.Pass,
			//            Text = _localizationService.GetResource("Admin.System.Warnings.MachineKey.Specified")
			//        });
			//    }
			//}
			//catch (Exception exc)
			//{
			//    LogException(exc);
			//}

			return View(model);
		}

		public virtual ActionResult Maintenance()
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			var model = new CommonMaintenanceModel();
			model.DeleteGuests.EndDate = DateTime.UtcNow.AddDays(-7);
			model.DeleteGuests.OnlyWithoutShoppingCart = true;
			model.DeleteAbandonedCarts.OlderThan = DateTime.UtcNow.AddDays(-182);
			return View(model);
		}

		[HttpPost, ActionName("Maintenance")]
		[FormValueRequired("delete-guests")]
		public virtual ActionResult MaintenanceDeleteGuests(CommonMaintenanceModel model)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			DateTime? startDateValue = (model.DeleteGuests.StartDate == null) ? null
							: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.DeleteGuests.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

			DateTime? endDateValue = (model.DeleteGuests.EndDate == null) ? null
							: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.DeleteGuests.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

			model.DeleteGuests.NumberOfDeletedCustomers = _customerService.DeleteGuestCustomers(startDateValue, endDateValue, model.DeleteGuests.OnlyWithoutShoppingCart);

			return View(model);
		}

		[HttpPost, ActionName("Maintenance")]
		[FormValueRequired("delete-abondoned-carts")]
		public virtual ActionResult MaintenanceDeleteAbandonedCarts(CommonMaintenanceModel model)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			var olderThanDateValue = _dateTimeHelper.ConvertToUtcTime(model.DeleteAbandonedCarts.OlderThan, _dateTimeHelper.CurrentTimeZone);

			model.DeleteAbandonedCarts.NumberOfDeletedItems = 0;
			return View(model);
		}

		[HttpPost, ActionName("Maintenance")]
		[FormValueRequired("delete-exported-files")]
		public virtual ActionResult MaintenanceDeleteFiles(CommonMaintenanceModel model)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			DateTime? startDateValue = (model.DeleteExportedFiles.StartDate == null) ? null
							: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.DeleteExportedFiles.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

			DateTime? endDateValue = (model.DeleteExportedFiles.EndDate == null) ? null
							: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.DeleteExportedFiles.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);


			model.DeleteExportedFiles.NumberOfDeletedFiles = 0;
			string path = Path.Combine(this.Request.PhysicalApplicationPath, "content\\files\\exportimport");
			foreach (var fullPath in Directory.GetFiles(path))
			{
				try
				{
					var fileName = Path.GetFileName(fullPath);
					if (fileName.Equals("index.htm", StringComparison.InvariantCultureIgnoreCase))
						continue;

					var info = new FileInfo(fullPath);
					if ((!startDateValue.HasValue || startDateValue.Value < info.CreationTimeUtc) &&
						(!endDateValue.HasValue || info.CreationTimeUtc < endDateValue.Value))
					{
						System.IO.File.Delete(fullPath);
						model.DeleteExportedFiles.NumberOfDeletedFiles++;
					}
				}
				catch (Exception exc)
				{
					ErrorNotification(exc, false);
				}
			}

			return View(model);
		}

		[HttpPost]
		public virtual ActionResult BackupFiles(DataSourceRequest command)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedKendoGridJson();

			var backupFiles = _maintenanceService.GetAllBackupFiles().ToList();

			var gridModel = new DataSourceResult
			{
				Data = backupFiles.Select(p => new
				{
					p.Name,
					Length = string.Format("{0:F2} Mb", p.Length / 1024f / 1024f),
					Link = _webHelper.GetStoreLocation(false) + "Administration/db_backups/" + p.Name
				}),
				Total = backupFiles.Count
			};
			return Json(gridModel);
		}

		[HttpPost, ActionName("Maintenance")]
		[FormValueRequired("backup-database")]
		public virtual ActionResult BackupDatabase(CommonMaintenanceModel model)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			try
			{
				_maintenanceService.BackupDatabase();
				this.SuccessNotification(_localizationService.GetResource("Admin.System.Maintenance.BackupDatabase.BackupCreated"));
			}
			catch (Exception exc)
			{
				ErrorNotification(exc);
			}

			return View(model);
		}

		[HttpPost, ActionName("Maintenance")]
		[FormValueRequired("backupFileName", "action")]
		public virtual ActionResult BackupAction(CommonMaintenanceModel model)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			var action = this.Request.Form["action"];

			var fileName = this.Request.Form["backupFileName"];
			var backupPath = _maintenanceService.GetBackupPath(fileName);

			try
			{
				switch (action)
				{
					case "delete-backup":
						{
							System.IO.File.Delete(backupPath);
							this.SuccessNotification(string.Format(_localizationService.GetResource("Admin.System.Maintenance.BackupDatabase.BackupDeleted"), fileName));
						}
						break;
					case "restore-backup":
						{
							_maintenanceService.RestoreDatabase(backupPath);
							this.SuccessNotification(_localizationService.GetResource("Admin.System.Maintenance.BackupDatabase.DatabaseRestored"));
						}
						break;
				}
			}
			catch (Exception exc)
			{
				ErrorNotification(exc);
			}

			return View(model);
		}

		[ChildActionOnly]
		public virtual ActionResult LanguageSelector()
		{
			var model = new LanguageSelectorModel();
			model.CurrentLanguage = _workContext.WorkingLanguage.ToModel();
			model.AvailableLanguages = _languageService
				.GetAllLanguages(storeId: _storeContext.CurrentStore.Id)
				.Select(x => x.ToModel())
				.ToList();
			return PartialView(model);
		}
		public virtual ActionResult SetLanguage(int langid, string returnUrl = "")
		{
			var language = _languageService.GetLanguageById(langid);
			if (language != null)
			{
				_workContext.WorkingLanguage = language;
			}

			//home page
			if (String.IsNullOrEmpty(returnUrl))
				returnUrl = Url.Action("Index", "Home", new { area = "Admin" });
			//prevent open redirection attack
			if (!Url.IsLocalUrl(returnUrl))
				return RedirectToAction("Index", "Home", new { area = "Admin" });
			return Redirect(returnUrl);
		}

		[HttpPost]
		public virtual ActionResult ClearCache(string returnUrl = "")
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			var cacheManager = EngineContext.Current.ContainerManager.Resolve<ICacheManager>("nop_cache_static");
			cacheManager.Clear();

			//home page
			if (String.IsNullOrEmpty(returnUrl))
				return RedirectToAction("Index", "Home", new { area = "Admin" });
			//prevent open redirection attack
			if (!Url.IsLocalUrl(returnUrl))
				return RedirectToAction("Index", "Home", new { area = "Admin" });
			return Redirect(returnUrl);
		}

		[HttpPost]
		public virtual ActionResult RestartApplication(string returnUrl = "")
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			//restart application
			_webHelper.RestartAppDomain();

			//home page
			if (String.IsNullOrEmpty(returnUrl))
				return RedirectToAction("Index", "Home", new { area = "Admin" });
			//prevent open redirection attack
			if (!Url.IsLocalUrl(returnUrl))
				return RedirectToAction("Index", "Home", new { area = "Admin" });
			return Redirect(returnUrl);
		}


		public virtual ActionResult SeNames()
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			var model = new UrlRecordListModel();
			return View(model);
		}
		[HttpPost]
		public virtual ActionResult SeNames(DataSourceRequest command, UrlRecordListModel model)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedKendoGridJson();

			var urlRecords = _urlRecordService.GetAllUrlRecords(model.SeName, command.Page - 1, command.PageSize);
			var gridModel = new DataSourceResult
			{
				Data = urlRecords.Select(x =>
				{
					//details URL
					string detailsUrl = "";
					//var entityName = x.EntityName != null ? x.EntityName.ToLowerInvariant() : "";
					//switch (entityName)
					//{
					//    case "article":
					//        detailsUrl = Url.Action("ArticleSizeEdit", "Article", new { id = x.EntityId });
					//        break;
					//    default:
					//        break;
					//}

					return new UrlRecordModel
					{
						Id = x.Id,
						Name = x.Slug,
						EntityId = x.EntityId,
						EntityName = x.EntityName,
						IsActive = x.IsActive,
						DetailsUrl = detailsUrl
					};
				}),
				Total = urlRecords.TotalCount
			};
			return Json(gridModel);
		}
		[HttpPost]
		public virtual ActionResult DeleteSelectedSeNames(ICollection<int> selectedIds)
		{
			if (!_permissionService.Authorize(StandardPermissionProvider.ManageMaintenance))
				return AccessDeniedView();

			if (selectedIds != null)
			{
				_urlRecordService.DeleteUrlRecords(_urlRecordService.GetUrlRecordsByIds(selectedIds.ToArray()));
			}

			return Json(new { Result = true });
		}


		[ChildActionOnly]
		public virtual ActionResult PopularSearchTermsReport()
		{
			return PartialView();
		}
		[HttpPost]
		public virtual ActionResult PopularSearchTermsReport(DataSourceRequest command)
		{
			return AccessDeniedKendoGridJson();

			//var searchTermRecordLines = _searchTermService.GetStats(command.Page - 1, command.PageSize);
			//var gridModel = new DataSourceResult
			//{
			//    Data = searchTermRecordLines.Select(x => new SearchTermReportLineModel
			//    {
			//        Keyword = x.Keyword,
			//        Count = x.Count,
			//    }),
			//    Total = searchTermRecordLines.TotalCount
			//};
			//return Json(gridModel);
		}


		//action displaying notification (warning) to a store owner that "limit per store" feature is ignored
		[ChildActionOnly]
		public virtual ActionResult MultistoreDisabledWarning()
		{

			return PartialView();
		}
		//action displaying notification (warning) to a store owner that "ACL rules" feature is ignored
		[ChildActionOnly]
		public virtual ActionResult AclDisabledWarning()
		{
			return PartialView();
		}

		//action displaying notification (warning) to a store owner that entered SE URL already exists
		[ValidateInput(false)]
		public virtual ActionResult UrlReservedWarning(string entityId, string entityName, string seName)
		{
			if (string.IsNullOrEmpty(seName))
				return Json(new { Result = string.Empty }, JsonRequestBehavior.AllowGet);

			int parsedEntityId;
			int.TryParse(entityId, out parsedEntityId);
			var validatedSeName = SeoExtensions.ValidateSeName(parsedEntityId, entityName, seName, null, false);

			if (seName.Equals(validatedSeName, StringComparison.InvariantCultureIgnoreCase))
				return Json(new { Result = string.Empty }, JsonRequestBehavior.AllowGet);

			return Json(new { Result = string.Format(_localizationService.GetResource("Admin.System.Warnings.URL.Reserved"), validatedSeName) }, JsonRequestBehavior.AllowGet);
		}

		#endregion
	}
}
